/*
Brick And Blast, a crossplatform breakout game.
 Copyright (C) 2008-2010 Julien CLEMENT (andematunum@gmail.com)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef _Options_h_
#define _Options_h_

/**
* file   Options.h
* author Julien CLEMENT
* date   Jeu 28 jan 2010 18:57:34 CET
*/

#include "SDL.h"

/** \defgroup OptionState
  * All the methods used exclusively
  * by the OptionState automaton.
  */

#define NOPTIONS (7)
enum OptionType{
  OPT_ENLARGE         = 0,
  OPT_STICK           = 1,
  OPT_SCOPE           = 2,
  OPT_LASER           = 3,
  OPT_SAVE            = 4,
  OPT_INVINCIBILITY   = 5,
  OPT_CRAZYBALL       = 6,
  OPT_NONE            = 9999,
};

/**
  * Event class for communicating between an option
  * and a client.
  */
class OptionEventListener {
  public:
    /**
      * Called whenever an option is requested by the user
      * and the request is accepted.
      */
    virtual void option_selected (const Option& option) = 0;
    /**
      * Called whenever an option looses all its upgrades
      * or reaches timeout.
      */
    virtual void option_cleared (const Option& option) = 0;
};

/**
  * This automaton manages a particular option,
  * its current upgrade level, elapsed time, and more.
  */
class Option {
public:
  /**
    * \ingroup OptionState
    * Build a new option of a given type.
    * An option can have various levels of
    * upgrades like the power of a laser beam,
    * and needs a certain number of credits
    * to be available to the player.
    * Also, certain options are bound in time,
    * like the invincibility.
    *
    * \param type Type of the option
    *             \sa OptionType
    * \param ncredits Number of credits
    *                 necessary for activating this
    *                 option
    * \param nupgrades Maximum number of upgrades
    *                  for this option
    * \param timelimit  Time span during which
    *                   the option is active,
    *                   zero for unbounded options.
    *                   (in milliseconds)
    */
  Option(OptionType type,
         Uint8 ncredits,
         Uint8 nupgrades,
         Uint32 timelimit_ms)
    : type_(type), ncredits_(ncredits),
      nupgrades_(nupgrades), timelimit_ms_(timelimit_ms) {
    clear();
  }
  /**
    * \ingroup OptionState
    * Call clear whenever the option is not
    * active anymore.
    * All the upgrades are lost, and the
    * time counter is reset.
    * \sa OptionEventListener
    */
  void clear () {
    upgrade_    = 0;
    start_time_ = 0;
    fireOptionCleared_ ();
  }
  /**
    * Call this function at each game frame.
    */
  void update () {
    if (timelimit_ms_ <= 0) {
      return;
    }
    if (SDL_GetTicks() - start_time_ >= timelimit_ms_) {
      clear();
    }
  }
  
  /**
    * \ingroup OptionState
    * Check if the option is available.
    * If it is so, the user will see it
    * highlighted and ready for selection.
    * Any option which is not available
    * is not proposed to the user for selection,
    * either because it's already upgraded at maximum,
    * or it's currently running.
    */
  bool available () {
    return (can_upgrade_() || !active());
  }
  /**
    * \ingroup OptionState
    * Select this option.
    * If it is time bounded, start the timer.
    * Raise the upgrade level.
    * \sa OptionEventListener
    */
  void select () {
    if (timelimit_ms_ > 0) {
      start_time_ = SDL_GetTicks();
    }
    ++upgrade_;
    fireOptionSelected_ ();
  }
  /**
    * Get the current upgrade level.
    * Use for upgradable options.
    */
  Uint8 get_ugprade_level () {
    return upgrade_;
  }
  /**
    * Check if the option is currently
    * active or not.
    * Use for time bounded options to
    * know if it's under execution.
    * For unbounded options, prefer using
    * get_upgrade_level which is more accurate.
    * \sa get_upgrade_level.
    */
  bool active () {
    return (upgrade_ > 0);
  }

private:
  /**
    * Check if the option can be upgraded.
    */
  bool can_upgrade_ () {
    return (upgrade_ < nupgrades_);
  }
  void fireOptionCleared_ () {
    for (set<OptionEventListener*>::iterator it = listeners_.begin();
         it != listeners_.end();
         it++) {
      OptionEventListener* l = *it;
      l->option_cleared(*this);
    }
  }
  void fireOptionSelected_ () {
    for (set<OptionEventListener*>::iterator it = listeners_.begin();
         it != listeners_.end();
         it++) {
      OptionEventListener* l = *it;
      l->option_selected(*this);
    }
  }

private:
  /** Inputs, see constructor */
  const OptionType type_;
  const Uint8 ncredits_;
  const Uint8 nupgrades_;
  const Uint32 timelimit_ms_;
  
  /** State */
  /** Current upgrade level */
  Uint8 upgrade_;
  /** Time tag for time-bounded options,
    * set when the option gets activated
    * by the player. */
  Uint32 start_time_;

  set<OptionEventListener*> listeners_;
};

class OptionState {
public:
    
  /** Call this function whenever an NRJ brick
    * is destroyed. */
  void energy_collected();
  /** Call this function whenever the player
    * attempts to select an option, even if
    * it's not possible.
    * \return true when the request is satisfied
    */
  bool request_option();
  /** Call this at each frame so that we update
    * the time counter for each time bounded
    * option.
    * \param timeout_list List of the time-bounded options
    *                     for which the time is over.
    */
  void update(vector<OptionType>& timeout_list);

private:
  /**
    * Currently selected option
    */
  OptionType current_option_;
  /**
    * Current number of NRJ credits
    */
  unsigned short credits_;
};

#endif

